前言

Status Bar 在 iOS 7 扁平化风格之后有了很大变化,变成了跟 NavigationBar 融为一体的风格,当然也可以隐藏掉。至于实现的方法,从开始到现在有了很多变化,因此也就有了很多的坑。之前都是在网上搜索教程,试验失败就再找,成功的话就 Copy 过来,原理什么也不是很懂。而且 Apple 这坑货,经常使把 API 变来变去的,这个版本好用,下个版本就给你改没了。所以我就把我研究的结果在这里总结一下。

环境:Xcode 8,iOS 9 & iOS 10,Swift 3

局部控制状态栏

首先要知道 info.plist 中可以添加这么一个条目:
View controller-based status bar appearance,默认值为 YES。这个从字面意思理解就是是否通过 ViewController 来控制 Status Bar 的样式。当设置为 YES 或者不手动添加条目时,我们就可以在当前的 ViewController 里重载变量来控制 Bar 的 Style、是否隐藏以及动画类型。重载的代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
//控制 Style
override var preferredStatusBarStyle: UIStatusBarStyle{
return .lightContent
}
//控制隐藏
override var prefersStatusBarHidden: Bool{
return true
}
//控制动画
override var preferredStatusBarUpdateAnimation: UIStatusBarAnimation{
return .slide
}

这种方法在 iOS7 ~ iOS10 都有效,但是只对执行了重载的 ViewController 有效果,并不是全局生效。

大多数时候我们都需要全局设置样式,那么以上这种重载的方法就不起作用了。

全局控制状态栏

iOS7~iOS9 中,全局控制状态栏的改变,只需要两步:

  1. 添加属性 View controller-based status bar appearance,设置为 NO
  2. 添加如下代码:
    1
    2
    UIApplication.shared.statusBarStyle = .lightContent
    UIApplication.shared.isStatusBarHidden = true

上面方法最好放在 AppDelegate.swift 中,程序一开始就执行。

经测试,以上两步在 iOS10 中也生效,网上教程说失效是因为使用了setStatusBarStylesetStatusBarHidden 这两个方法(其实也不是失效,只是 Deprecated了,会有警告但是还生效)。

此外还可以这样:(iOS9 和 iOS10 都生效)

  1. 也是添加属性 View controller-based status bar appearance,并设置为 NO
  2. 不需写代码,只要在 Deployment Info 修改 Status Bar Style 即可

两种方法貌似有细微差别,第一种方法消失时有个 fade 效果,第二种则是运行时直接就不显示。

特殊情况

如果我们使用 NavigationController,会发现在原来的 ViewController 里修改状态栏的 style 不起作用了,但是控制状态栏的显示和隐藏依然OK。但是使用 TabBarController 依然正常,状态栏不受 TabBarController影响。

  • 全局方法依然生效(别忘了 info.plist 添加项设为 NO
  • 局部控制当前 ViewController 重载的方法部分失效

这里失效是这样的,之前没嵌入时,ViewController 中 Custom Class 是你的脚本,脚本中重载来控制此 ViewController 的 Status Bar。但是嵌入到 Nav 后,你的脚本不是此 NavController 的 Custom Class,因此重载不会生效。同时所有 push 到这个 NavController 中的 ViewController 都共用 NavController 的 Navigation Item,因此也都不会生效。

注意: prefersStatusBarHidden 和 preferredStatusBarUpdateAnimation 还生效。

题外话

我们看手机淘宝客户端,🔍宝贝的时候如果滑动屏幕,Navigation Bar 就会隐藏掉。有的 App 连 Status Bar 也会隐藏。那么如何隐藏 Navigation Bar 呢?在 viewDidLoad 中设置 navigationController?.hidesBarsOnSwipe = true 就行了。有趣的是,之前我们说过的嵌入到 NavigationController 后重载 preferredStatusBarStyle 让 Status Bar 显示 lightContent 的方法会失效,但是如果 hidesBarsOnSwipe 设为 true,向上滑动隐藏时,该方法又生效了。

那么如何在隐藏 navigationBar 时也隐藏 status bar 呢?请看代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
var hideStatusBar = false
override func viewDidLoad() {
super.viewDidLoad()
//滑动的手势,添加监听事件
navigationController?.barHideOnSwipeGestureRecognizer.addTarget(self, action: #selector(swipe))
navigationController?.hidesBarsOnSwipe = true
}
// 在 swipe 方法中更改 hideStatusBar 的值
override var prefersStatusBarHidden: Bool {
return hideStatusBar
}
// Status Bar 隐藏的动画效果
override var preferredStatusBarUpdateAnimation: UIStatusBarAnimation {
return .slide
}
func swipe(recognizer: UISwipeGestureRecognizer) {
//获得 navigationBar 的原点的 y 坐标值。iOS 中左上角为 (0.0, 0.0)点,y轴向下。
//这里的 origin 在不隐藏时是 (0.0, 20.0),隐藏后为 (0.0, -44.0)
//因此 navBar 隐藏后使 hideStatusBar 为 true,执行一下 Update,就会把 statusBar 也隐藏
let originY:CGFloat! = navigationController?.navigationBar.frame.origin.y
hideStatusBar = originY < CGFloat(0)
UIView.animate(withDuration: 0.2) {
self.setNeedsStatusBarAppearanceUpdate()
}
}

参考资料

iOS 填坑系列 - 状态栏变化
iOS 知识小集(Status Bar变换)
iOS Status Bar 的隐藏